package com.newly.center.sys.service.domain.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.newly.center.sys.service.authEntity.api.IAuthEntityService;
import com.newly.center.sys.service.domain.api.IDomainService;
import com.newly.common.base.constants.AuthConstant;
import com.newly.common.base.constants.CommonConstant;
import com.newly.common.base.entity.auth.ICurrentTenant;
import com.newly.common.base.entity.sys.domain.dto.DomainDto;
import com.newly.common.base.entity.sys.domain.po.DomainPo;
import com.newly.common.core.utils.ConverseUtils;
import com.newly.common.core.utils.RequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @program: newly-parent
 * @remark: 域
 * @author: guan
 * @create: 2022-01-14 09:59
 */
@Service
@Slf4j
public class DomainService extends AbstractDomainService implements IDomainService {

    @Autowired
    private RequestUtils requestUtils;

    @Autowired
    private IAuthEntityService authEntityService;

    @Override
    public boolean save(DomainDto domainDto) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        DomainPo domainPo = BeanUtil.copyProperties(domainDto, DomainPo.class);
        if (ObjectUtil.isEmpty(domainPo.getParentId())) {
            domainPo.setParentId(CommonConstant.DOMAIN_PARENT_FLAG);
        } else {
            // 当前租户必须拥有指定的上级区域
            currentTenant.assertIncludeEntity(AuthConstant.AuthEntityType.DOMAIN,
                    domainDto.getParentId().toString());
        }
        domainPo.setCreateBy(currentTenant.getUserId());
        domainPo.setCreateTime(DateUtil.date());
        int insert = domainMapper.insert(domainPo);
        if (insert > 0) {
            // 绑定权限实体
            boolean result = authEntityService.bind(AuthConstant.AuthEntityType.DOMAIN,
                    currentTenant.getSelfRoleId(), domainPo.getId().toString());
            Assert.isTrue(result, "Binding permission error!");
        }
        return insert > 0;
    }

    @Override
    public boolean modifyById(DomainDto domainDto) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        currentTenant.assertIncludeEntity(AuthConstant.AuthEntityType.DOMAIN,
                domainDto.getId().toString());
        // 需要级联操作的数据
        DomainPo domainPo = BeanUtil.copyProperties(domainDto, DomainPo.class);
        boolean result = this.modifyLinkageProperties(domainPo);
        Assert.isTrue(result, "Failed to modify linkage property of domain!");

        int update = domainMapper.updateById(domainPo);
        return update > 0;
    }

    @Override
    public boolean modifyLinkageProperties(DomainPo domainPo) {
        if (!ObjectUtil.isEmpty(domainPo.getEnable())) {
            List<DomainPo> packageDomainPos = new ArrayList<>();
            this.getChildren(Arrays.asList(domainPo.getId()), packageDomainPos);
            packageDomainPos.add(domainPo);
            this.modifyEnableById(parseIds(packageDomainPos), domainPo.getEnable());
        }
        return true;
    }

    @Override
    public boolean modifyEnableById(List<Integer> domainIds, Integer enable) {
        if (ObjectUtil.isEmpty(domainIds)) return false;
        LambdaQueryWrapper<DomainPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(DomainPo::getId, domainIds);
        DomainPo domainPo = new DomainPo();
        domainPo.setEnable(enable);
        int update = domainMapper.update(domainPo, wrapper);
        return update > 0;
    }

    @Transactional
    @Override
    public boolean removeByIds(List<Integer> domainIds) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        currentTenant.assertIncludeEntities(AuthConstant.AuthEntityType.DOMAIN,
                ConverseUtils.toString(domainIds));

        // 获取所有子域
        List<DomainPo> childrenDomainPo = new ArrayList<>();
        getChildren(domainIds, childrenDomainPo);

        List<Integer> delIds = new ArrayList<>();
        delIds.addAll(domainIds);
        if (!ObjectUtil.isEmpty(childrenDomainPo)) {
            delIds.addAll(parseIds(childrenDomainPo));
        }

        int delete = domainMapper.deleteBatchIds(delIds);
        if (delete > 0) {
            // 删除权限实体
            boolean result = authEntityService.unBind(AuthConstant.AuthEntityType.DOMAIN,
                    ConverseUtils.toString(delIds));
            Assert.isTrue(result, "Unbinding domain error!");
        }
        return delete > 0;
    }


    @Override
    public List<DomainPo> getByIds(List<? extends Serializable> domainId) {
        return domainMapper.selectBatchIds(domainId);
    }

    @Override
    public DomainPo getById(Integer domainId) {
        return domainMapper.selectById(domainId);
    }

    @Override
    public DomainPo getByIdInAuth(Integer domainId) {
        List<DomainPo> domainPos = getByIdsInAuth(Arrays.asList(domainId));
        if (!ObjectUtil.isEmpty(domainPos)) {
            return domainPos.get(0);
        }
        return null;
    }

    @Override
    public List<DomainPo> getByIdsInAuth(List<Integer> domainIds) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        currentTenant.assertIncludeEntities(AuthConstant.AuthEntityType.DOMAIN,
                ConverseUtils.toString(domainIds));
        return this.getByIds(domainIds);
    }

    @Override
    public List<DomainPo> getByParentIds(List<Integer> parentDomainIds) {
        if (ObjectUtil.isEmpty(parentDomainIds)) return null;

        LambdaQueryWrapper<DomainPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(DomainPo::getParentId, parentDomainIds);
        return domainMapper.selectList(wrapper);
    }

    @Override
    public List<DomainPo> getByParentId(Integer parentDomainId) {
        return getByParentIds(Arrays.asList(parentDomainId));
    }

    @Override
    public List<DomainPo> listInAuth(String keyword, String fromTime, String toTime) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        List<String> entityIds = currentTenant.getEntityIds(AuthConstant.AuthEntityType.DOMAIN);
        if (ObjectUtil.isEmpty(entityIds)) return null;

        LambdaQueryWrapper<DomainPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(DomainPo::getCreateTime);
        wrapper.in(DomainPo::getId, entityIds);
        // 关键字
        if (!StrUtil.isBlank(keyword)) {
            wrapper.like(DomainPo::getName, keyword);
        }
        // 创建时间范围
        if (!StrUtil.isBlank(fromTime) && !StrUtil.isBlank(toTime)) {
            wrapper.between(DomainPo::getCreateTime, fromTime, toTime);
        }

        List<DomainPo> domainPos = domainMapper.selectList(wrapper);
        if (ObjectUtil.isEmpty(domainPos)) return null;

        // 获取到所有域id
        List<Integer> tempDomainIds = new ArrayList<>(domainPos.size());
        for (DomainPo domainPo : domainPos) {
            tempDomainIds.add(domainPo.getId());
        }

        List<DomainPo> allDomainPo = new ArrayList<>();
        for (DomainPo domainPo : domainPos) {
            // 封装子域,如果当前域的父域不存在tempDomainIds中，说明是顶级域
            if (!tempDomainIds.contains(domainPo.getParentId())) {
                List<DomainPo> children = packageChildren(domainPo, domainPos);
                domainPo.setChildren(children);
                allDomainPo.add(domainPo);
            }
        }
        return allDomainPo;
    }

    @Override
    public List<DomainPo> packageChildren(DomainPo parentDomainPo, List<DomainPo> allDomainPo) {
        if (ObjectUtil.isEmpty(parentDomainPo) || ObjectUtil.isEmpty(allDomainPo)) return null;

        List<DomainPo> childrenDomainPo = new ArrayList<>();
        for (DomainPo domainPo : allDomainPo) {
            if (parentDomainPo.getId().equals(domainPo.getParentId())) {
                List<DomainPo> children = packageChildren(domainPo, allDomainPo);
                domainPo.setChildren(children);
                childrenDomainPo.add(domainPo);
            }
        }

        return childrenDomainPo;
    }

    @Override
    public void getChildren(List<Integer> parentDomainIds, List<DomainPo> packageDomainPos) {
        if (ObjectUtil.isEmpty(parentDomainIds)) return;

        for (Integer parentDomainId : parentDomainIds) {
            List<DomainPo> childrenDomain = getByParentId(parentDomainId);
            if (!ObjectUtil.isEmpty(childrenDomain)) {
                getChildren(parseIds(childrenDomain), packageDomainPos);
                packageDomainPos.addAll(childrenDomain);
            }
        }
    }

}
